Skip to main content

Docker Basics for Spring Boot Applications

What is Docker?

Docker is a containerization platform that packages applications and their dependencies into lightweight, portable containers. For Spring Boot applications, Docker provides consistent deployment environments across development, testing, and production.

Core Docker Concepts

Images

  • Definition: Read-only templates used to create containers
  • Spring Boot Context: Contains your JAR file, JVM, and runtime dependencies
  • Example: openjdk:17-jre-slim as base image for Spring Boot apps

Containers

  • Definition: Running instances of Docker images
  • Spring Boot Context: Your application running in an isolated environment
  • Lifecycle: Created, started, stopped, and removed

Dockerfile

  • Definition: Text file with instructions to build Docker images
  • Purpose: Automates the image creation process

Essential Dockerfile for Spring Boot

Basic Dockerfile Structure

# Use official OpenJDK runtime as base image
FROM openjdk:17-jre-slim

# Set working directory inside container
WORKDIR /app

# Copy the JAR file into container
COPY target/myapp-1.0.0.jar app.jar

# Expose the port Spring Boot runs on
EXPOSE 8080

# Command to run the application
ENTRYPOINT ["java", "-jar", "app.jar"]
# Build stage
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# Runtime stage
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Optimized Dockerfile with Layers

FROM openjdk:17-jre-slim

# Create non-root user for security
RUN groupadd -r spring && useradd -r -g spring spring

WORKDIR /app

# Copy dependencies first (better caching)
COPY target/dependency/ ./
COPY target/classes ./classes/
COPY target/*.jar app.jar

# Change ownership to spring user
RUN chown -R spring:spring /app
USER spring

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-cp", "/app/classes:/app/dependency/*", "com.example.Application"]

Essential Docker Commands

Image Operations

# Build image from Dockerfile
docker build -t myapp:latest .

# List images
docker images

# Remove image
docker rmi myapp:latest

# Pull image from registry
docker pull openjdk:17-jre-slim

Container Operations

# Run container
docker run -p 8080:8080 myapp:latest

# Run in detached mode
docker run -d -p 8080:8080 --name myapp-container myapp:latest

# List running containers
docker ps

# List all containers
docker ps -a

# Stop container
docker stop myapp-container

# Remove container
docker rm myapp-container

# Execute command in running container
docker exec -it myapp-container bash

Development Commands

# Run with environment variables
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=dev myapp:latest

# Mount volume for logs
docker run -p 8080:8080 -v $(pwd)/logs:/app/logs myapp:latest

# Run with network
docker run -p 8080:8080 --network mynetwork myapp:latest

Docker Compose for Spring Boot

Basic docker-compose.yml

version: '3.8'

services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
depends_on:
- database
networks:
- app-network

database:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network

volumes:
postgres_data:

networks:
app-network:
driver: bridge

Development-Focused docker-compose.yml

version: '3.8'

services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8080:8080"
- "5005:5005" # Debug port
environment:
- SPRING_PROFILES_ACTIVE=dev
- JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
volumes:
- .:/app
- /app/target
depends_on:
- database
- redis

database:
image: postgres:13
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev123
ports:
- "5432:5432"
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql

redis:
image: redis:7-alpine
ports:
- "6379:6379"

Spring Boot Docker Integration

Application Properties for Docker

# application-docker.properties
spring.datasource.url=jdbc:postgresql://database:5432/myapp
spring.datasource.username=${DB_USER:user}
spring.datasource.password=${DB_PASSWORD:password}

# Redis configuration
spring.redis.host=redis
spring.redis.port=6379

# Actuator for health checks
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always

Maven Configuration

Add to pom.xml:

<properties>
<docker.image.name>myapp</docker.image.name>
</properties>

<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.name}:${project.version}</name>
</image>
</configuration>
</plugin>

<!-- Docker Maven Plugin -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>${docker.image.name}</repository>
<tag>${project.version}</tag>
</configuration>
</plugin>
</plugins>
</build>

Best Practices for Spring Boot + Docker

1. Security

# Use non-root user
RUN addgroup --system spring && adduser --system spring --ingroup spring
USER spring:spring

2. Health Checks

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s \
CMD curl -f http://localhost:8080/actuator/health || exit 1

3. Efficient Layering

# Copy dependencies separately for better caching
COPY target/dependency/ ./dependency/
COPY target/classes ./classes/
COPY target/*.jar app.jar

4. Environment-Specific Images

# Development
docker build -f Dockerfile.dev -t myapp:dev .

# Production
docker build -f Dockerfile.prod -t myapp:prod .

Common Docker Commands for Development

Quick Development Workflow

# Build and run
docker build -t myapp . && docker run -p 8080:8080 myapp

# Using docker-compose
docker-compose up --build

# Rebuild and restart specific service
docker-compose up --build app

# View logs
docker-compose logs -f app

# Clean up
docker-compose down
docker system prune -f

Debugging

# Run with debug port exposed
docker run -p 8080:8080 -p 5005:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" myapp

# Access container shell
docker exec -it container-name bash

# Check logs
docker logs container-name -f

Docker Networking for Spring Boot

Creating Networks

# Create custom network
docker network create myapp-network

# Run containers on same network
docker run --network myapp-network --name database postgres:13
docker run --network myapp-network -p 8080:8080 myapp

Service Discovery

In docker-compose, services can communicate using service names:

spring.datasource.url=jdbc:postgresql://database:5432/myapp

Volume Management

Persistent Data

# Create named volume
docker volume create myapp-data

# Use volume
docker run -v myapp-data:/app/data myapp

Development Volumes

# Mount source code for hot reload
docker run -v $(pwd):/app -p 8080:8080 myapp:dev

Deployment Considerations

Production Dockerfile

FROM openjdk:17-jre-slim

# Security updates
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*

# Non-root user
RUN groupadd -r spring && useradd -r -g spring spring

WORKDIR /app
COPY --chown=spring:spring target/*.jar app.jar

USER spring

EXPOSE 8080

# Production JVM options
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"

ENTRYPOINT exec java $JAVA_OPTS -jar app.jar

Environment Variables

# Production deployment
docker run -d \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_HOST=prod-db-host \
-e DB_PASSWORD=secure-password \
--name myapp-prod \
myapp:latest

This guide covers the essential Docker concepts and practical examples needed for developing and deploying Spring Boot applications effectively.